/*
 * Semaphore.cpp
 *
 *  Created on: 2011/06/04
 *      Author: naoki
 */

#include "NTEngine/NTEngine.h"
#include "NTEngine/Threading/Semaphore.h"
#include "NTEngine/Base/ReferenceCounter.h"

#include <pthread.h>

namespace NTEngine {
namespace Threading {

class Semaphore::Impl : public ReferenceCounter {
public:
	Impl( int startCount, int maxCount )
	: mMaxCount( maxCount )
	, mCount( startCount ) {
		pthread_mutex_init( &mMutex, NULL );
		pthread_cond_init( &mCondition, NULL );
	}

	~Impl() {
		pthread_cond_destroy( &mCondition );
		pthread_mutex_destroy( &mMutex );
	}

	void decrease() {
		pthread_mutex_lock( &mMutex );
		while ( mCount == 0 ) { // セマフォ値が0のときは待つ(block)
			pthread_cond_wait( &mCondition, &mMutex );
		}
		mCount -= 1; // セマフォ値を1減らす(ロック数を増やす)
		pthread_mutex_unlock( &mMutex );
	}

	void increase( int v ) {
		int prev; // 直前の値をチェックする用
		pthread_mutex_lock( &mMutex );
		prev = mCount;
		mCount += v; // セマフォ値をv増やす(ロックを解除)
		if ( v == 1 ) {
			// 1だったら一個だけに伝えればいいだろう
			pthread_cond_signal( &mCondition );
		} else {
			// それより多かったらとりあえず全体に通知する
			pthread_cond_broadcast( &mCondition );
		}
		pthread_mutex_unlock( &mMutex );
		HARD_ASSERT( ( prev + v ) <= mMaxCount );
	}

	const int mMaxCount;
	int mCount;
	pthread_mutex_t mMutex;
	pthread_cond_t mCondition;
};

Semaphore Semaphore::create( int startCount, int maxCount ) {
	HARD_ASSERT( startCount >= 0 );
	if ( maxCount < startCount ) {
		maxCount = startCount;
	}
	Semaphore ret;
	ret.mImpl = NEW Impl( startCount, maxCount );
	return ret;
}

void Semaphore::decrease() {
	mImpl->decrease();
}

void Semaphore::increase( int v ) {
	ASSERT( v > 0 );
	mImpl->increase( v );
}

#define TYPE Semaphore
#include "NTEngine/Base/Impl/ReferenceCounterTemplate.h"

} // namespace Threading
} // namespace NTEngine
